package cn.xz.ruler.demo.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewConfiguration
import android.widget.Scroller
import cn.xz.ruler.demo.view.RulerView
import kotlin.math.abs

/**
 * 可滑动的刻度尺
 *
 * @author xiaozhi
 * @date 20211011
 */
class RulerView : View {

    companion object {
        private val TAG = RulerView::class.java.simpleName
    }

    private var startX = 0
    private var downX = 0
    private var downY = 0
    private var finalX = 0
    private var finalY = 0

    /**
     * 平缓滑动
     */
    private var mScroller: Scroller? = null

    /**
     * 计算惯性滑动速率
     */
    private var mVelocityTracker: VelocityTracker? = null

    /**
     * 最小惯性滑动速率
     */
    private var mMinimumFlingVelocity = 0

    /**
     * 最大惯性滑动速率
     */
    private var mMaximumFlingVelocity = 0

    /**
     * 判定为滑动的最小移动像素数
     */
    private var mTouchSlop = 0

    /**
     * x轴滚动速率
     */
    private var xVelocity = 0

    /**
     * y轴滚动速率
     */
    private var yVelocity = 0

    /**
     * 背景Paint
     */
    private var mPaint: Paint? = null

    /**
     * 指针Paint
     */
    private var mPointerPaint: Paint? = null

    /**
     * 刻度Paint
     */
    private var mScalePaint: Paint? = null

    /**
     * 背景颜色
     */
    private val mBgColor = -0x222223

    /**
     * 刻度颜色
     */
    private val mScaleColor = -0xaaaaab

    /**
     * 指针颜色
     */
    private val mPointerColor = -0x10000

    /**
     * 刻度文字大小
     */
    private val mTextSize = 48

    /**
     * view宽
     */
    private var viewWidth = 0

    /**
     * view高
     */
    private var viewHeight = 0

    /**
     * 开始滚动
     */
    private var isStartScroll = false

    /**
     * 表示目前处于fling的滑动中.
     */
    private var isFling = false

    /**
     * 每个刻度之间的间距，单位px
     */
    private var mLineSpace = 30

    /**
     * 总刻度100cm
     */
    private var mScaleNum = 0

    /**
     * 最小刻度值
     */
    private var mMinScale = 450

    /**
     * 最大刻度值
     */
    private var mMaxScale = 2000

    /**
     * 当前指向刻度
     */
    private var mCurScale = 1000

    /**
     * 预绘制刻度数
     */
    private var mPreScaleCount = 30

    constructor(context: Context) : super(context) {
        init(context)
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init(context)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        init(context)
    }

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes) {
        init(context)
    }

    /**
     * 设置一个刻度的像素值
     *
     * @param lineSpace
     */
    fun setLineSpace(lineSpace: Int) {
        mLineSpace = lineSpace
    }

    /**
     * 设置总刻度值
     *
     * @param scaleNum
     */
    fun setScaleNum(scaleNum: Int) {
        mScaleNum = scaleNum
    }

    /**
     * 设置刻度值
     *
     * @param minScale
     */
    fun setMinScale(minScale: Int) {
        mMinScale = minScale
        mScaleNum = mMaxScale - mMinScale
        invalidate()
    }

    /**
     * 设置最大刻度值
     *
     * @param maxScale
     */
    fun setMaxScale(maxScale: Int) {
        mMaxScale = maxScale
        mScaleNum = mMaxScale - mMinScale
        invalidate()
    }

    /**
     * 设置当前指向刻度
     *
     * @param curScale
     */
    fun setCurScale(curScale: Int) {
        mCurScale = curScale
        mScroller!!.startScroll(0, 0, (mCurScale - mMinScale) * mLineSpace, 0, 1000)
        invalidate()
    }

    private fun init(context: Context) {
        mScroller = Scroller(context)
        val configuration = ViewConfiguration.get(context)
        mMinimumFlingVelocity = configuration.scaledMinimumFlingVelocity / 10
        mMaximumFlingVelocity = configuration.scaledMaximumFlingVelocity / 10
        mTouchSlop = configuration.scaledTouchSlop
        mPaint = Paint()
        mPaint!!.textSize = 60f
        mPaint!!.color = Color.RED
        mPaint!!.strokeWidth = 20f
        mPointerPaint = Paint()
        mPointerPaint!!.color = mPointerColor
        mPointerPaint!!.strokeWidth = 6f
        mScalePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mScalePaint!!.color = mScaleColor
        mScalePaint!!.strokeWidth = 2f
        mScalePaint!!.textSize = mTextSize.toFloat()
        mScaleNum = mMaxScale - mMinScale
        mLineSpace = dp2px(8f)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        var heightSize = MeasureSpec.getSize(heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val mode = MeasureSpec.getMode(heightMeasureSpec)
        if (mode == MeasureSpec.AT_MOST) {
            heightSize = dp2px(54f)
        }
        setMeasuredDimension(widthSize, heightSize)
        viewWidth = measuredWidth
        viewHeight = measuredHeight

        // 计算预绘制刻度数
        val preScaleCount = viewWidth / 2 / mLineSpace
        mPreScaleCount = Math.ceil((preScaleCount / 2f).toDouble()).toInt() * 2
        Log.i(TAG, "预绘制数：$preScaleCount 实际绘制数：$mPreScaleCount")
        mScroller!!.startScroll(0, 0, (mCurScale - mMinScale) * mLineSpace, 0, 1000)
        invalidate()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawColor(mBgColor)

        // 绘制指针
        canvas.drawLine(
            viewWidth * 0.5f + scrollX,
            viewHeight * 0.25f,
            viewWidth * 0.5f + scrollX,
            viewHeight.toFloat(),
            mPointerPaint!!
        )
        var curScaleNum = scrollX / mLineSpace
        if (curScaleNum > mScaleNum - mPreScaleCount) {
            curScaleNum = mScaleNum - mPreScaleCount
        }
        var j = if (curScaleNum - mPreScaleCount > 0) curScaleNum - mPreScaleCount else 0
        while (j >= 0 && j <= curScaleNum + mPreScaleCount) {
            when {
                (j + mMinScale) % 10 == 0 -> {
                    val num = (j + mMinScale) / 10
                    // 画长刻度
                    canvas.drawLine(
                        viewWidth * 0.5f + (num * 10 - mMinScale) * mLineSpace,
                        viewHeight * 0.6f,
                        viewWidth * 0.5f + (num * 10 - mMinScale) * mLineSpace,
                        viewHeight.toFloat(),
                        mScalePaint!!
                    )
                    // 画刻度数字
                    val textWidth = mScalePaint!!.measureText(num.toString() + "").toInt()
                    canvas.drawText(
                        num.toString() + "",
                        viewWidth * 0.5f + (num * 10 - mMinScale) * mLineSpace - textWidth / 2f,
                        viewHeight * 0.56f,
                        mScalePaint!!
                    )
                }
                (j + mMinScale) % 5 == 0 -> {
                    canvas.drawLine(
                        viewWidth * 0.5f + j * mLineSpace,
                        viewHeight * 0.7f,
                        viewWidth * 0.5f + j * mLineSpace,
                        viewHeight.toFloat(),
                        mScalePaint!!
                    )
                }
                else -> {
                    canvas.drawLine(
                        viewWidth * 0.5f + j * mLineSpace,
                        viewHeight * 0.8f,
                        viewWidth * 0.5f + j * mLineSpace,
                        viewHeight.toFloat(),
                        mScalePaint!!
                    )
                }
            }
            j++
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val x = event.x.toInt()
        val y = event.y.toInt()
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                mScroller!!.forceFinished(true)
                isFling = false
                startX = x
                downX = x
                downY = y
                acquireVelocityTracker(event)
                parent.requestDisallowInterceptTouchEvent(true)
            }
            MotionEvent.ACTION_MOVE -> {
                val moveX = x - startX
                val diff = Math.abs(x - downX).toFloat()
                // 滑动距离过小，不触发滑动
                if (diff < mTouchSlop && !isStartScroll) {
                    parent.requestDisallowInterceptTouchEvent(false)
                    return true
                }
                isStartScroll = true
                parent.requestDisallowInterceptTouchEvent(true)
                if (mScroller!!.isFinished) {
                    mScroller!!.startScroll(
                        mScroller!!.finalX,
                        mScroller!!.finalY,
                        -moveX,
                        0, 0
                    )
                }
                invalidate()
                startX = x
                acquireVelocityTracker(event)
            }
            MotionEvent.ACTION_UP -> {
                // 计算速率
                mVelocityTracker!!.computeCurrentVelocity(1000, mMaximumFlingVelocity.toFloat())
                // x轴方向速率
                xVelocity = mVelocityTracker!!.xVelocity.toInt()
                // y轴方向速率
                yVelocity = mVelocityTracker!!.yVelocity.toInt()
                // 当滑动速率大于最小速率开始惯性滑动
                if (abs(xVelocity) >= mMinimumFlingVelocity) {
                    // 触发惯性滑动
                    Log.d(TAG, "触发惯性滑动... 实际xVelocity = $xVelocity")
                    isFling = true
                    mScroller!!.fling(
                        scrollX,
                        scrollY,
                        -xVelocity,
                        0,
                        0,
                        mScaleNum * mLineSpace,
                        -height,
                        height
                    )
                    invalidate()
                } else {
                    Log.e(TAG, "无惯性滑动")
                    isFling = false
                    scrollFinish()
                }
                finalX = mScroller!!.finalX
                finalY = mScroller!!.finalY
                parent.requestDisallowInterceptTouchEvent(false)
                releaseVelocityTracker()
                isStartScroll = false
            }
            MotionEvent.ACTION_CANCEL -> {
                parent.requestDisallowInterceptTouchEvent(false)
                releaseVelocityTracker()
                isStartScroll = false
            }
        }
        return true
    }

    override fun computeScroll() {
        super.computeScroll()
        if (mScroller!!.computeScrollOffset()) { //判断滚动是否完成，true说明滚动尚未完成，false说明滚动已经完成
            if (mScroller!!.currX == mScroller!!.finalX && isFling) {
                isFling = false
                scrollFinish()
            }
            scrollTo(mScroller!!.currX, 0) // 将view直接移动到当前滚动的位置
            val scaleNum = scrollX / mLineSpace
            notifyScaleChange(mMinScale + scaleNum)
            invalidate()
        }
    }

    // 滑动停止后重新定位
    private fun scrollFinish() {
        val scrollX = scrollX
        val scaleNum = scrollX / mLineSpace
        val distance = scrollX - scaleNum * mLineSpace
        Log.i(TAG, "距离刻度：$distance scaleNum = $scaleNum scrollX = $scrollX")
        val scrollDistance: Int
        val curScale: Int
        if (scaleNum >= mScaleNum) {
            Log.i(TAG, "滚动到右边界")
            scrollDistance = mScaleNum * mLineSpace - scrollX
            curScale = mMaxScale
        } else if (scaleNum <= 0) {
            Log.i(TAG, "滚动到左边界：$distance")
            scrollDistance = 0 - scrollX
            curScale = mMinScale
        } else {
            if (distance < mLineSpace / 2) {
                Log.i(TAG, "滚动到左边刻度")
                scrollDistance = -distance
                curScale = mMinScale + scaleNum
            } else {
                Log.i(TAG, "滚动到右边刻度")
                scrollDistance = mLineSpace - distance
                curScale = mMinScale + scaleNum + 1
            }
        }
        mScroller!!.startScroll(getScrollX(), 0, scrollDistance, 0, 300)
        invalidate()
        notifyScale(curScale)
    }

    private fun acquireVelocityTracker(event: MotionEvent) {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain()
        }
        mVelocityTracker!!.addMovement(event)
    }

    private fun releaseVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker!!.clear()
            mVelocityTracker!!.recycle()
            mVelocityTracker = null
        }
    }

    interface OnScaleChangeListener {
        fun onScaleFinish(scale: Int)
        fun onScaleChange(scale: Int)
    }

    private fun notifyScale(scale: Int) {
        mCurScale = scale
        if (mOnScaleChangeListener != null) {
            mOnScaleChangeListener!!.onScaleFinish(mCurScale)
        }
    }

    private fun notifyScaleChange(scale: Int) {
        if (mOnScaleChangeListener != null) {
            mOnScaleChangeListener!!.onScaleChange(scale)
        }
    }

    private var mOnScaleChangeListener: OnScaleChangeListener? = null
    fun setOnScaleChangeListener(onScaleChangeListener: OnScaleChangeListener?) {
        mOnScaleChangeListener = onScaleChangeListener
    }

    private fun dp2px(dp: Float): Int {
        return (context.resources.displayMetrics.density * dp + 0.5f).toInt()
    }
}